package com.gitee.deament.server.core.cli.db.sortset;

import cn.hutool.core.util.NumberUtil;
import com.gitee.deament.server.core.annotation.CliHander;
import com.gitee.deament.server.core.cli.AbstractCli;
import com.gitee.deament.server.core.data.ConnectData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.util.List;

/**
 * ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
 * 可用版本： >= 1.0.5
 * 时间复杂度: O(log(N)+M)， N 为有序集的基数， M 为被结果集的基数。
 * 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
 * <p>
 * 具有相同 score 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的，不需要额外的计算)。
 * <p>
 * 可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count )，注意当 offset 很大时，定位 offset 的操作可能需要遍历整个有序集，此过程最坏复杂度为 O(N) 时间。
 * <p>
 * 可选的 WITHSCORES 参数决定结果集是单单返回有序集的成员，还是将有序集成员及其 score 值一起返回。 该选项自 Redis 2.0 版本起可用。
 * <p>
 * 区间及无限
 * min 和 max 可以是 -inf 和 +inf ，这样一来，你就可以在不知道有序集的最低和最高 score 值的情况下，使用 ZRANGEBYSCORE 这类命令。
 * <p>
 * 默认情况下，区间的取值使用闭区间 (小于等于或大于等于)，你也可以通过给参数前增加 ( 符号来使用可选的开区间 (小于或大于)。
 * 举个例子：
 * <p>
 * ZRANGEBYSCORE zset (1 5
 * 返回所有符合条件 1 < score <= 5 的成员，而
 * <p>
 * ZRANGEBYSCORE zset (5 (10
 * 则返回所有符合条件 5 < score < 10 的成员。
 * 返回值
 * 指定区间内，带有 score 值(可选)的有序集成员的列表。
 */
@CliHander(command = "ZRANGEBYSCORE")
public class ZRANGEBYSCOREcli extends AbstractCli {
    @Override
    public void executeCli(ChannelHandlerContext channelHandlerContext, String command) {
        List<String> cliValues = getValues(command);
        //俩个不定项 一共四种情况
        if (cliValues.size() != 4 && cliValues.size() != 5 && cliValues.size() != 3 && cliValues.size() != 8) {
            channelHandlerContext.channel().writeAndFlush(new TextWebSocketFrame("ERR wrong number of arguments for 'ZRANGEBYSCORE' command"));
            return;
        }
        String key = cliValues.get(1);
        String min = cliValues.get(2);
        String max = cliValues.get(3);
        ZRANGEBYSCOREparam params=parseParams(cliValues);
        //校验参数
        if(!params.isParamIsRight()){
            channelHandlerContext.channel().writeAndFlush(new TextWebSocketFrame("ERR wrong number of arguments for 'ZRANGEBYSCORE' command"));
            return;
        }
        int db = ConnectData.SELECT_DB_STORAGE.getUseDB(channelHandlerContext.channel().id().asLongText());
    }

    public ZRANGEBYSCOREparam parseParams(List<String> cliValues) {
        ZRANGEBYSCOREparam param=    new ZRANGEBYSCOREparam();
        if (cliValues.size() == 4) {
            return param;
        }
        if (cliValues.size() == 5) {
            if (cliValues.get(4).equalsIgnoreCase("WITHSCORES")) {
                param.setWithScores(true);
            }
            param.setParamIsRight(false);
            return param;
        }
        if (cliValues.size() == 7) {
            if (cliValues.get(4).equalsIgnoreCase("LIMIT")) {
                String offsetStr = cliValues.get(5);
                String countStr = cliValues.get(6);
                if (NumberUtil.isNumber(offsetStr) && NumberUtil.isNumber(countStr)) {
                    param.setLimit(true);
                    param.setOffset(Integer.parseInt(offsetStr));
                    param.setCount(Integer.parseInt(countStr));
                    return param;
                }
            }
            param.setParamIsRight(false);
            return param;
        }
        if (cliValues.size() == 7) {
            boolean withScoresCheck=false;
            boolean limitCheck=false;
            if (cliValues.get(4).equalsIgnoreCase("WITHSCORES")||cliValues.get(7).equalsIgnoreCase("WITHSCORES")) {
                withScoresCheck= true;
            }
            int limitIndex=4;
            if (cliValues.get(limitIndex).equalsIgnoreCase("LIMIT")) {
                String offsetStr = cliValues.get(limitIndex+1);
                String countStr = cliValues.get(limitIndex+2);
                param.setOffset(Integer.parseInt(offsetStr));
                param.setCount(Integer.parseInt(countStr));
                if (NumberUtil.isNumber(offsetStr) && NumberUtil.isNumber(countStr)) {
                    limitCheck= true;
                }
            }
            limitIndex=5;
            if (cliValues.get(limitIndex).equalsIgnoreCase("LIMIT")) {
                String offsetStr = cliValues.get(limitIndex+1);
                String countStr = cliValues.get(limitIndex+2);
                param.setOffset(Integer.parseInt(offsetStr));
                param.setCount(Integer.parseInt(countStr));
                if (NumberUtil.isNumber(offsetStr) && NumberUtil.isNumber(countStr)) {
                    limitCheck= true;
                }
            }
            if(withScoresCheck&&limitCheck) {
                param.setParamIsRight(true);
                param.setWithScores(true);
                param.setLimit(true);
            }
        }
        param.setParamIsRight(false);
        return param;
    }


}

class ZRANGEBYSCOREparam{
    boolean paramIsRight=true;
    boolean withScores;
    boolean limit;
    int offset;
    int count;

    public boolean isParamIsRight() {
        return paramIsRight;
    }

    public void setParamIsRight(boolean paramIsRight) {
        this.paramIsRight = paramIsRight;
    }

    public boolean isWithScores() {
        return withScores;
    }

    public void setWithScores(boolean withScores) {
        this.withScores = withScores;
    }

    public boolean isLimit() {
        return limit;
    }

    public void setLimit(boolean limit) {
        this.limit = limit;
    }

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}
